package com.atguigu.gulimall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.es.SkuEsModel;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.search.config.ElasticSearchConfig;
import com.atguigu.gulimall.search.feign.ProductFeignService;
import com.atguigu.gulimall.search.service.MallSearchService;
import com.atguigu.gulimall.search.vo.AttrResponseVo;
import com.atguigu.gulimall.search.vo.SearchParam;
import com.atguigu.gulimall.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author 满超
 * @date 2020/06/07
 **/
@Service
public class MallSearchServiceImpl implements MallSearchService {

    @Qualifier("restHighLevelClient")
    @Autowired
    private RestHighLevelClient client;

    @Autowired
    ProductFeignService feignService;

    @Override
    public SearchResult search(SearchParam param) {
        SearchResult result = null;

        //1.构建检索请求
        SearchRequest searchRequest = builderSearchRequest(param);

        try {
            //执行检索
            SearchResponse searchResponse = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
            
            //检索结果
            result = builderSearchResult(searchResponse, param);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }


    /**
     * 准备检索请求
     *
     * @return
     */
    private SearchRequest builderSearchRequest(SearchParam param) {
        System.out.println("初始值"+ param);

        //索引条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        /**
         * 查询，过滤，模糊匹配
         */
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (!org.springframework.util.StringUtils.isEmpty(param.getKeyword())) {
            boolQuery.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
        }
        if (param.getCatalog3Id() != null) {
            boolQuery.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
        }
        if (param.getBrandId() != null && param.getBrandId().size() > 0) {
            boolQuery.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
        }

        boolQuery.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));

        if (!StringUtils.isEmpty(param.getSkuPrice())) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
            String[] s = param.getSkuPrice().split("_");//[1,500]
            if (s.length == 2 && !Objects.equals(s[0], "")){
                rangeQuery.gte(s[0]);
                rangeQuery.lte(s[1]);
            }
            if (s.length == 2 && Objects.equals(s[0], "")) {
                if (param.getSkuPrice().startsWith("_")) {
                    rangeQuery.lte(s[1]);
                } else if (param.getSkuPrice().endsWith("_")) {
                    rangeQuery.gte(s[0]);
                }
            }
            boolQuery.filter(rangeQuery);
        }
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            for (String attr : param.getAttrs()) {
                BoolQueryBuilder nestedQueryBuilder = QueryBuilders.boolQuery();

                String[] s = attr.split("_");//attr=1_16GB;8GB
                String attrId = s[0];
                String[] value = s[1].split(":");

                nestedQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attrId));
                nestedQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", value));

                //每一个都得生成查询
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedQueryBuilder, ScoreMode.None);
                boolQuery.filter(nestedQuery);
            }
        }

        //封装条件
        sourceBuilder.query(boolQuery);


        /**
         * 排序，高亮，分页
         */
        //排序
        if (!org.springframework.util.StringUtils.isEmpty(param.getSort())) {
            String sort = param.getSort();//sort=result_asc/desc
            String[] s = sort.split("_");
            SortOrder order = s[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;
            sourceBuilder.sort(s[0], order);

        }
        //分页
        sourceBuilder.from((param.getPageNum() - 1) * 8);//form = (pageNum-1)*size
        sourceBuilder.size(8);
        //高亮
        if (!StringUtils.isEmpty(param.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");//前置标签
            highlightBuilder.postTags("</b>");//后置标签

            sourceBuilder.highlighter(highlightBuilder);
        }

        /**
         * 聚合分析
         */
        //品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        sourceBuilder.aggregation(brand_agg);
        //分类聚合
        //TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
        //catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg")).field("catalogName.keyword").size(1);
        //sourceBuilder.aggregation(catalog_agg);

        //属性聚合
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));
        attr_agg.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(attr_agg);

        String s = sourceBuilder.toString();
        System.out.println("构建的查询语句" + s);

        return new SearchRequest(new String[]{"product"}, sourceBuilder);
    }

    /**
     * 封装查询结果
     *
     * @param searchResponse
     * @return
     */
    private SearchResult builderSearchResult(SearchResponse searchResponse, SearchParam param) {

        SearchResult result = new SearchResult();
        List<SkuEsModel> skuEsModelList = new ArrayList<>();
        //所有查询到的商品
        SearchHits hits = searchResponse.getHits();//总命中数
        if (hits.getHits() != null && hits.getHits().length > 0) {
            for (SearchHit hit : hits.getHits()) {
                String source = hit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(source, SkuEsModel.class);//json转为SkuEsModel
                if (!StringUtils.isEmpty(param.getKeyword())) {
                    //高亮字段
                    HighlightField highlightField = hit.getHighlightFields().get("skuTitle");
                    String s = highlightField.getFragments()[0].toString();
                    skuEsModel.setSkuTitle(s);
                }
                skuEsModelList.add(skuEsModel);
            }
        }
        result.setProducts(skuEsModelList);
        //分类信息
        //List<SearchResult.CatalogVo> catalogVoList = new ArrayList<>();
        //ParsedLongTerms catalog_agg = searchResponse.getAggregations().get("catalog_agg");
        //for (Terms.Bucket bucket : catalog_agg.getBuckets()) {
        //    SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
        //    //分类id
        //    String keyAsString = bucket.getKeyAsString();
        //    //分类名
        //    ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
        //    String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();
        //
        //    catalogVo.setCatalogName(catalog_name);
        //    catalogVo.setCatalogId(Long.parseLong(keyAsString));
        //
        //    catalogVoList.add(catalogVo);
        //}
        //result.setCatalogs(catalogVoList);
        //品牌信息
        List<SearchResult.BrandVo> brandVoList = new ArrayList<>();
        ParsedLongTerms brand_agg = searchResponse.getAggregations().get("brand_agg");
        for (Terms.Bucket bucket : brand_agg.getBuckets()) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            //品牌id
            String keyAsString = bucket.getKeyAsString();
            //品牌图片
            ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
            String brand_pic = brand_img_agg.getBuckets().get(0).getKeyAsString();
            //品牌名字
            ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
            String brand_name = brand_name_agg.getBuckets().get(0).getKeyAsString();

            brandVo.setBrandId(Long.parseLong(keyAsString));
            brandVo.setBrandImg(brand_pic);
            brandVo.setBrandName(brand_name);


            brandVoList.add(brandVo);
        }
        result.setBrands(brandVoList);


        //属性信息
        List<SearchResult.AttrVo> attrVoList = new ArrayList<>();
        ParsedNested attr_agg = searchResponse.getAggregations().get("attr_agg");
        ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
        for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            //属性id
            long attrID = bucket.getKeyAsNumber().longValue();
            //属性名字
            String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
            //属性值
            List<String> attrValue = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(MultiBucketsAggregation.Bucket::getKeyAsString).collect(Collectors.toList());


            attrVo.setAttrId(attrID);
            attrVo.setAttrName(attrName);
            attrVo.setAttrValue(attrValue);

            attrVoList.add(attrVo);
        }
        result.setAttrs(attrVoList);
        //分页信息
        result.setPageNum(param.getPageNum());//页码
        long total = hits.getTotalHits().value;//总条数
        result.setTotal(total);
        //分页信息
        int totalPages = (int) total % 8 == 0 ? (int) total / 8 : ((int) total / 8 + 1);
        result.setTotalPage(totalPages);

        //导航条页码
        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 1; i <= totalPages; i++) {
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);

        if (param.getAttrs() != null && param.getAttrs().size() >0) {
            //面包屑导航
            List<SearchResult.NavVo> navVoList = param.getAttrs().stream().map(attr -> {
                //1.得到attr传过来的参数
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                String[] s = attr.split("_");//attr=1_16GB;8GB
                navVo.setNavValue(s[1]);

                //远程调用attr
                R info = feignService.info(Long.parseLong(s[0]));
                if (info.getCode() == 0) {
                    //正常返回
                    AttrResponseVo responseVo = info.getData("attr", new TypeReference<>() {
                    });
                    navVo.setNavName(responseVo.getAttrName());
                } else {
                    navVo.setNavName(s[0]);//把id作为名字
                }

                //2.取消面包屑我们要跳转到的页面，删除地址栏的属性
                String encode = URLEncoder.encode(attr, StandardCharsets.UTF_8);
                String link = param.get_queryString().replace("&attrs=" + encode, "");
                navVo.setLink("http://search.gulimall.com/list.html?"+link);

                return navVo;
            }).collect(Collectors.toList());

            result.setNavs(navVoList);
        }

        System.out.println("结果是"+result);

        return result;
    }
}
